#pragma once

#include <nuraft.hxx>

#include "KeeperBasic.h"

namespace keeper_server {

using string = std::string;

enum KeeperMessageType {
  KEEPER_MESSAGE_TYPE_START = 0,
  KV_MESSAGE,
  STR_MESSAGE
};

namespace {
string enumToStr(KeeperMessageType type) {
  switch (type) {
    case KEEPER_MESSAGE_TYPE_START:
      return "KEEPER_MESSAGE_TYPE_START";
    case KV_MESSAGE:
      return "KV_MESSAGE";
    case STR_MESSAGE:
      return "STR_MESSAGE";
    default:
      return "Unknown type";
  }
}
}  // namespace

class KeeperMessage {
 public:
  KeeperMessage(KeeperMessageType type = KEEPER_MESSAGE_TYPE_START,
                int version = 0)
      : msg_type(type), msg_version(version) {}

  void set_message_version(int version) { msg_version = version; }
  void set_message_type(KeeperMessageType type) { msg_type = type; }
  int get_message_version() { return msg_version; }
  KeeperMessageType get_message_type() { return msg_type; }

  nuraft::ptr<nuraft::buffer> serlialize_message() {
    std::cout << get_message_size() << std::endl;
    nuraft::ptr<nuraft::buffer> new_log =
        nuraft::buffer::alloc(get_message_size());
    nuraft::buffer_serializer bs(new_log);
    bs.put_i32(msg_type);
    bs.put_i32(msg_version);
    do_serlialize_message(bs);
    return new_log;
  }

  void deserlialize_message(nuraft::buffer &data) {
    nuraft::buffer_serializer bs(data);
    msg_type = KeeperMessageType(bs.get_i32());
    msg_version = bs.get_i32();

    do_deserlialize_message(bs);
  }

  size_t get_message_size() {
    return sizeof(uint) + sizeof(nuraft::int32) + do_get_message_size();
  }

  void printInfo() {
    std::cout << "MessageType:" << enumToStr(msg_type)
              << ", version:" << msg_version << std::endl;
    do_printInfo();
  }

 private:
  virtual void do_serlialize_message(nuraft::buffer_serializer &bs) = 0;
  virtual void do_deserlialize_message(nuraft::buffer_serializer &bs) = 0;
  virtual size_t do_get_message_size() { return 0; }
  virtual void do_printInfo() = 0;

 protected:
  KeeperMessageType msg_type;
  int msg_version;
};

class KVMessage : public KeeperMessage {
 public:
  KVMessage(const string &k, const string &v,
            KeeperMessageType type = KV_MESSAGE, int version = 0)
      : KeeperMessage(type, version), key(k), value(v) {}

  KVMessage() {}
  void do_serlialize_message(nuraft::buffer_serializer &bs);
  void do_deserlialize_message(nuraft::buffer_serializer &bs);

  size_t do_get_message_size() {
    return key.length() + value.length() + sizeof(int) * 2;
  }
  void do_printInfo() { std::cout << key << "," << value << std::endl; }
  std::string &get_key() { return key; }
  std::string &get_value() { return value; }

 private:
  string key;
  string value;
};

class StrMessage : public KeeperMessage {
 public:
  StrMessage(const string &s, KeeperMessageType type = STR_MESSAGE,
             int version = 0)
      : KeeperMessage(type, version), msg_str(s) {}
  StrMessage() {}
  void do_serlialize_message(nuraft::buffer_serializer &bs) {
    bs.put_str(msg_str);
  }
  void do_deserlialize_message(nuraft::buffer_serializer &bs) {
    msg_str = bs.get_str();
  }

  size_t do_get_message_size() { return msg_str.length() + sizeof(int); }
  void do_printInfo() { std::cout << msg_str << std::endl; }

 private:
  string msg_str;
};

}  // namespace keeper_server
